﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "ActionCoordinateInputWidget.h"
#include "IUiView.h"
#include "IUiDocumentViewManager.h"

#include "CoordinateInputEditor.h"
#include "CoordinateInputDefinition.h"
#include "CoordinateInputValidator.h"
#include "NumberValidator.h"
#include "GbmpConstants.h"

#include <QHBoxLayout>
#include <QApplication>
#include <QLabel>
#include <QtLineEdit.h>
#include <QKeyEvent>
#include "GcmpUiQtImpl.h"

#include "GcmpUiDocumentImpl.h"
#include "GnufUIView.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

//////////////////////////////////////////////////////////////////////////

ActionCoordinateInputWidget::ActionCoordinateInputWidget(QWidget *parent)
    : QDialog(parent)
    , m_pBoxLayout(nullptr)
    , m_pDefaultEdit(nullptr)
    , m_pCurLineEdit(nullptr)
    , m_editCallBack(nullptr)
    , m_userCustomInput(false)
    , m_isDeleteLater(false)
{
    setObjectName("ActionCoordinateInputWidget");

    //添加布局器
    QBoxLayout* pBoxLayout = NEW_AS_QT_CHILD(QHBoxLayout, this);
    SetLayout(pBoxLayout);

    setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
    setFocusPolicy(Qt::NoFocus);
    setAttribute(Qt::WA_TranslucentBackground);// 透明背景

    // 设置事件过滤器
    installEventFilter(this);
    if (parent)
    {
        parent->installEventFilter(this);
    }
}

ActionCoordinateInputWidget::~ActionCoordinateInputWidget()
{
    m_pDefaultEdit = nullptr;
    m_pCurLineEdit = nullptr;

    removeEventFilter(this);
    for (auto& pControl : m_controls)
    {
        pControl->removeEventFilter(this);
    }
    if (parent())
    {
        parent()->removeEventFilter(this);
    }
}

void ActionCoordinateInputWidget::SetWindowTitle(const std::wstring& title)
{
    if (title.empty())
        return;

    // 添加标题
    if (m_opTitleLabel == nullptr)
    {
        m_opTitleLabel = NEW_AS_OWNER_PTR(QLabel, QString::fromStdWString(title), this);
        m_opTitleLabel->setBaseSize(1, 22);
        m_opTitleLabel->adjustSize();

        // 设置标题底色及边框
        m_opTitleLabel->setStyleSheet("background-color:rgb(128,128,128,128);border:1px solid black;");

        QBoxLayout* pBoxLayout = GetLayout();
        pBoxLayout->insertWidget(0, m_opTitleLabel.get(), 0, Qt::AlignLeft | Qt::AlignVCenter);
        pBoxLayout->addSpacing(5);
    }

    m_opTitleLabel->setText(QString::fromStdWString(title));
}

void ActionCoordinateInputWidget::InitWidget(std::vector<gcmp::OwnerPtr<gcmp::ICoordinateInputDefinition>>&& dlgCtrlDefines,
    std::function<bool(const std::wstring &editValue, double &coorDinate, std::wstring &errorTip)> &editCallBack, bool userCustomInput/* = false*/)
{
    // 清理原有控件
    QBoxLayout* pBoxLayout = GetLayout();
    if (pBoxLayout != nullptr)
    {
        ClearControl();
    }
    else
    {
        //添加布局器
        pBoxLayout = NEW_AS_QT_CHILD(QHBoxLayout, this);
        SetLayout(pBoxLayout);
    }

    m_editCallBack = editCallBack;
    m_userCustomInput = userCustomInput;

    for (auto & opCtrlDef : dlgCtrlDefines)
    {
        if (opCtrlDef == nullptr)
            continue;

        const CoordinateInputDefinition *pCtrlDef = dynamic_cast<const CoordinateInputDefinition*>(opCtrlDef.get());
        DBG_WARN_AND_CONTINUE_UNLESS(pCtrlDef, L"pCtrlDef为空",L"GDMPLab",L"2024-03-30");

        gcmp::OwnerPtr<CoordinateInputEditor> opLineEditWidget = NEW_AS_OWNER_PTR(CoordinateInputEditor, 
            TransferOwnershipCast<CoordinateInputDefinition>(opCtrlDef), this);
        opLineEditWidget->GetEditorWidget()->setReadOnly(true); //默认只读模式
        opLineEditWidget->GetEditorWidget()->setFocusPolicy(Qt::NoFocus);
        opLineEditWidget->GetEditorWidget()->installEventFilter(this);
        opLineEditWidget->GetEditorWidget()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);//显示内容置于左对齐和垂直居中

        if (QBoxLayout* pBoxLayout = GetLayout())
        {
            pBoxLayout->addWidget(opLineEditWidget->GetEditorWidget(), 0, Qt::AlignLeft | Qt::AlignVCenter);
            pBoxLayout->setSpacing(5);
        }

        m_controls.push_back(TransferOwnership(opLineEditWidget));
        m_controlDefines.emplace_back(pCtrlDef);
    }

    adjustSize();
}

bool ActionCoordinateInputWidget::CheckEditInputValidate()
{
    if (!m_pDefaultEdit)
        return true;

    CoordinateInputDefinition *pCtrlDef = m_pDefaultEdit.Get();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pCtrlDef, L"pCtrlDef为空",L"GDMPLab",L"2024-03-30");
    std::function<std::wstring()> getTipCallBefore = [pCtrlDef](void)->std::wstring {
        return pCtrlDef->GetText();
    };

    bool isDouble = false;
    QString text = QString::fromStdWString(pCtrlDef->GetText());
    double digitVal = text.toDouble(&isDouble);

    // 合法性检查
    bool isValidate = false;
    {
        pCtrlDef->SetValidator(nullptr);// 先清理当前检查器

        OwnerPtr<ValidatorBase> opValidator;
        if (m_editCallBack)
        {
            opValidator = NEW_AS_OWNER_PTR(CoordinateInputValidator, m_editCallBack, getTipCallBefore);
        }
        else //默认坐标参数检查
        {
            if (m_userCustomInput == false)
            {
                opValidator = NEW_AS_OWNER_PTR(NumberValidator<double>, -gcmp::Constants::MAX_MODEL_SIZE, gcmp::Constants::MAX_MODEL_SIZE);
            }
        }
        pCtrlDef->SetValidator(TransferOwnership(opValidator));
        if (pCtrlDef->GetValidator())
        {
            isValidate = pCtrlDef->GetValidator()->ValidateValue(digitVal);
        }
    }

    // 忽略非法输入
    if (!IsUserCustomInput() && (!isDouble || !isValidate))
    {
        m_pDefaultEdit->SelectAll();
        return false;
    }

    return true;
}

void ActionCoordinateInputWidget::OnEditFinished()
{
}

void ActionCoordinateInputWidget::ClearControl()
{ 
    QLayout* pLayout = layout();
    if (pLayout != nullptr)
    {
        for (auto& pControl : m_controls)
        {
            if (QWidget* pWidget = dynamic_cast<QWidget*>(pControl.get()))
            {
                pLayout->removeWidget(pWidget); // remove from layout
                pWidget->deleteLater();//避免对话框上子控件重影
                pWidget->hide(); //触发日志记录正常
            }
        }

        m_controls.clear();
        pLayout->invalidate();  
    }
}

void ActionCoordinateInputWidget::Show()
{
    this->show();
}
void ActionCoordinateInputWidget::Hide()
{
    hide();
}
void ActionCoordinateInputWidget::HideAndClear()
{
    hide();
    m_pDefaultEdit = nullptr;
    m_pCurLineEdit = nullptr;
    m_controls.clear();
    // 标记被延迟删除
    m_isDeleteLater = true;
}

void ActionCoordinateInputWidget::SetLayout(QBoxLayout* pLayout)
{
    if (pLayout != nullptr)
    {
        m_pBoxLayout = pLayout;
        
        pLayout->setContentsMargins(10, 10, 10, 10);//修改布局器默认边框距离
        setLayout(pLayout);
    }
}

bool ActionCoordinateInputWidget::WakeOn(const std::wstring& keyStr)
{
    bool res = false;
    if (CoordinateInputDefinition* pDefEdit = GetDefaultEdit())
    {
        for (auto& pControl : m_controls)
        {
            if (pControl->GetControlDefinition() == pDefEdit)
            { 
                pControl->GetEditorWidget()->setReadOnly(false);
                pControl->GetEditorWidget()->setFocus();

                // 锁定状态用于锁定用户有效输入，一旦用户有效输入即刻解锁
                pDefEdit->SetLock(false);
                pDefEdit->SetText(keyStr);

                m_pCurLineEdit = pControl->GetEditorWidget();
                res = true;
            }
            else
            {
                pControl->GetEditorWidget()->setReadOnly(true);
            }
        }
        
    }

    return res;
}

void ActionCoordinateInputWidget::ActiveWindow() const
{
    FOR_EACH_INDEX(index, m_controls)
    {
        const gcmp::OwnerPtr<gcmp::CoordinateInputEditor> &pCoordinateInputEditor = m_controls[index];
        if (pCoordinateInputEditor->GetControlDefinition() == m_pDefaultEdit.Get())
        {
            QtLineEdit *pLineEdit = (const_cast<QtLineEdit*>(pCoordinateInputEditor->GetEditorWidget()));
            if (pLineEdit && !pLineEdit->isActiveWindow())
            {
                pLineEdit->activateWindow();
            }
            break;
        }
    }
}

bool gcmp::ActionCoordinateInputWidget::IsInInputState() const
{ 
    //未处于编辑状态的情况下
    if ((m_pDefaultEdit && !m_pCurLineEdit))
    {
        ActiveWindow();
        return false;
    }
    if (m_pCurLineEdit && !m_pCurLineEdit->isActiveWindow())
    {
        (const_cast<ActionCoordinateInputWidget*>(this))->m_pCurLineEdit = nullptr;
        return false;
    }
    return m_pCurLineEdit?true:false;
}

void gcmp::ActionCoordinateInputWidget::keyPressEvent(QKeyEvent * pEvent)
{
    if (pEvent->key() == Qt::Key_Tab)
    {
        if (!CheckEditInputValidate()) // 合法性检查
            return;

        QLineEdit* pPreLineEdit = dynamic_cast<QLineEdit*>(focusWidget());
        CoordinateInputDefinition* pNextDefEdit = SwitchToNextDefaultEdit();

        if (m_pCurLineEdit)
        {
            m_pCurLineEdit->setModified(false);
            m_pCurLineEdit = nullptr;
        }
        for (auto &pCtrl : m_controls)
        {
            pCtrl->GetEditorWidget()->setReadOnly(true);
        }

        // 将tab事件传给父控件
        QApplication::sendEvent(parent(), pEvent);

        // 防止焦点被抢走
        for (auto &pCtrl : m_controls)
        {
            if (pCtrl->GetControlDefinition() == GetDefaultEdit())
            {
                pCtrl->GetEditorWidget()->setFocus();
            }
        }
    }
}

void ActionCoordinateInputWidget::SetFocusToCurrentView()
{
    IUiView* pCurrentView = IUiDocumentViewManager::Get()->GetCurrentUiView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
    QWidget * pView = QWidget::find((WId)pCurrentView->GetUiViewId());
    if (pView)
    {
        pView->setFocus();
    }
}

bool ActionCoordinateInputWidget::eventFilter(QObject *object, QEvent *event)
{
    // 如果标记被延迟删除，则不处理消息
    if (m_isDeleteLater)
    {
        return false;
    }

    IUiView* pUIView = IUiDocumentViewManager::Get()->GetCurrentUiView();
    if (nullptr == pUIView)
    {
        return false;
    }


    QWidget* widget = QWidget::find(pUIView->GetUiViewId());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(widget, L"widget为空",L"GDMPLab",L"2024-03-30");
    
    if (dynamic_cast<QWidget*>(object) == widget)
    {
        if (event && event->type() == QEvent::Leave)
        {
            Hide();
        }  
        return false;
    }

    if (QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(event))
    {
        if (!IsInInputState())
        {
            return QApplication::sendEvent(widget, event);
        }
    }

    if (QWheelEvent* pWheelEvent = dynamic_cast<QWheelEvent*>(event))
    {
        QPoint pos = widget->mapFromGlobal(pWheelEvent->globalPos());
        int delta = pWheelEvent->delta();
        Qt::MouseButtons buttons = pWheelEvent->buttons();
        Qt::KeyboardModifiers modifiers = pWheelEvent->modifiers();
        QWheelEvent wheelEvent(QPointF(pos.x(), pos.y()), delta, buttons, modifiers, pWheelEvent->orientation());

        return QApplication::sendEvent(widget, &wheelEvent);
    }

    // 键盘事件拦截处理
    if (event && event->type() == QEvent::KeyPress)
    {
        QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Escape) // Escape键转发给视图，用于取消Action
        {
            hide();
            return QApplication::sendEvent(widget, event);
        }
        else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Space)
        {
            if ( !CheckEditInputValidate() ) // 合法性检查
            {
                return false; 
            }

            if (m_pCurLineEdit)
            {
                m_pCurLineEdit->setModified(false);
                m_pCurLineEdit = nullptr;
            }

            hide();
            return QApplication::sendEvent(widget, event);
        }
        else if (keyEvent->key() == Qt::Key_Tab || (keyEvent->modifiers() & Qt::ControlModifier))
        {
             return false;
        }
        else if (keyEvent->modifiers() & Qt::AltModifier)
        {
            return QApplication::sendEvent(widget, event);
        }

        // 键盘过滤 filter
        if (!IsInInputState())
        {
            int nKeyChar = keyEvent->key();
            bool isNumKey = (Qt::Key_0 <= nKeyChar && nKeyChar <= Qt::Key_9);
            bool isNumPadKey = (VK_NUMPAD0 <= nKeyChar && nKeyChar <= VK_NUMPAD9);
            bool isCommaKey = (nKeyChar == VK_OEM_COMMA);
            bool isMinusKey = (nKeyChar == Qt::Key_Minus) || (nKeyChar == Qt::Key_onehalf);
            bool isCharKey = (Qt::Key_A <= nKeyChar && nKeyChar <= Qt::Key_Z);
            if (!isNumKey && !isNumPadKey && !isCommaKey && !isMinusKey && !isCharKey)
            {
                return QApplication::sendEvent(widget, event);
            }
        }

        if (m_pCurLineEdit == nullptr)
        {
            QString keyText = keyEvent->text();
            return WakeOn(keyText.toStdWString());
        }
    }
    return QDialog::eventFilter(object, event);
}

void ActionCoordinateInputWidget::Refresh()
{ 
    QPoint cursorPos = QCursor::pos();//获取当前光标的位置
    if (QWidget* pParentWidget = dynamic_cast<QWidget*>(parent()))
    {
        bool isSubUiView = false;
        QObject* pPParentWidget = pParentWidget->parent();
        if (pPParentWidget)
        {

#ifdef _GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_
            IUiView* pParentUiView = gcmp::UIView::castTo<IUiView>(pPParentWidget);
#else
            IUiView* pParentUiView = dynamic_cast<IUiView*>(pPParentWidget);
#endif // !_GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_
           
            if (pParentUiView)
                isSubUiView = true;             //目前视口属于子UiView,其位置会被移到屏幕以外，导致视口中子控件看不见
        }

        QPoint pos;
        if (isSubUiView)
            pos = pParentWidget->mapFromGlobal(QPoint(cursorPos.x() + SUBUIVIEW_POSITION_OFFSET_X, cursorPos.y() + SUBUIVIEW_POSITION_OFFSET_Y));
        else
            pos = pParentWidget->mapFromGlobal(cursorPos);

        QRect rect;
        //坐标输入框的显示判断逻辑需要与视口窗口的数据值保持一致，进而根据鼠标位置是否在窗口内部，来决定是否显示坐标输入框
        if (isSubUiView)
        {
#ifdef _GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_
            IUiView* pSubUiView = gcmp::UIView::castTo<IUiView>(pParentWidget);
#else
            IUiView* pSubUiView = dynamic_cast<IUiView*>(pParentWidget);
#endif // !_GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_
            
            if (pSubUiView)
                rect = QRect(0, 0, pSubUiView->GetWindowWidth(), pSubUiView->GetWindowHeight());
        }
        else
            rect = pParentWidget->rect();
        if (rect.contains(pos, true))
        {
            show();
            move(QPoint(cursorPos.x() + 1, cursorPos.y() + 1));
        }
        else
        {
            hide();
        }
    }

    // 重新设置字符显示
    for (auto &pCtrl : m_controls)
    {
        pCtrl->GetEditorWidget()->setCursorPosition(0);
    }

    CoordinateInputDefinition* pDefEdit = GetDefaultEdit();
    if (pDefEdit == nullptr)
    {
        pDefEdit = SwitchToNextDefaultEdit();
    }

    if (pDefEdit)
    {
        pDefEdit->SelectAll();
    }
}

CoordinateInputDefinition* ActionCoordinateInputWidget::GetDefaultEdit()
{
    return m_pDefaultEdit.Get();
}

CoordinateInputDefinition* ActionCoordinateInputWidget::SwitchToNextDefaultEdit()
{
    if (m_controlDefines.empty())
        return nullptr;

    CoordinateInputDefinition* pDefEdit = GetDefaultEdit();
    auto next = m_controlDefines.end();
    for (auto it = m_controlDefines.begin(); it != m_controlDefines.end(); ++it)
    {
        if (pDefEdit == (*it).Get())
        {
            next = ++it;
            break;
        }   
    }

    //清理当前select状态
    for (auto& pCtrl : m_controls)
    {
        if (pCtrl->GetControlDefinition() == pDefEdit)
        {
            bool isModified = pCtrl->GetEditorWidget()->isModified();
            pCtrl->GetControlDefinition()->SetLock(isModified);
            pCtrl->GetEditorWidget()->deselect(); 
            break;
        }
    }

    if (next == m_controlDefines.end())
    {
        next = m_controlDefines.begin();
    }

    m_pDefaultEdit = *next;
    if (m_pDefaultEdit)
    {
    m_pDefaultEdit->SelectAll();
    }
    
    return m_pDefaultEdit.Get();
}